package evs1.restframework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class RestFramework extends HttpServlet
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -3177562475355784316L;
	
	private AnnotationChecker annotationChecker = new AnnotationChecker();
	private HibernateConnector hc = new HibernateConnector();
	private XStream xmlParser;
	private XStream jsonParser;
	
	@Override
	public void init() throws ServletException
	{
		annotationChecker.scanForAnnotations();
		xmlParser = new XStream(new DomDriver());
		jsonParser = new XStream(new JsonHierarchicalStreamDriver());
	}
	
	@Override
	public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		PrintWriter out = res.getWriter();
		long id;
		
		// search -> perform full text search
		if (req.getParameterMap().size() > 0) 
		{
		    doFullTextSearch(req, res);
		    return;
		}
		// validate uri (/entityname/id)
		String[] uri = req.getRequestURI().substring(req.getContextPath().length() + 1).split("/");
		if(uri.length != 2)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Wrong uri: " + uri);
			return;
		}
		// load id
		try {
			id = Long.parseLong(uri[1]);
		}catch(NumberFormatException nfe)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Wrong id: " + uri[1]);
			return;
		}
		// load classname for specified uri
		Class<?> theClass = annotationChecker.getClassname(uri[0]);
		
		if(theClass == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
			return;
		}
		//HibernateConnector hc = new HibernateConnector();
		Object obj = hc.getFromHibernate(theClass, id);
		
		if(req.getContentType() == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
			return;
		} 
		// json or xml?
		if(req.getContentType().equals("application/xml"))
		{
			res.setContentType("application/xml");
			out.print(xmlParser.toXML(obj));
			out.close();
			return;
		}else if (req.getContentType().equals("application/json"))
		{
			res.setContentType("application/json");
			out.print(jsonParser.toXML(obj));
			out.close();
			return;
		}
		
		// neither json nor xml -> error
		res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
		out.print("Invalid content type: " + req.getContentType());
		out.close();
		return;
	}
	
	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		PrintWriter out = res.getWriter();
		
		String str = "";
		String inputStr = "";
		BufferedReader in = req.getReader();
		Object obj = null;
		while((str = in.readLine()) != null)
			inputStr += str;
		
		// json or xml?
		if(req.getContentType().equals("application/xml"))
		{
			obj = xmlParser.fromXML(inputStr);
			res.setContentType("application/xml");						
		}else if (req.getContentType().equals("application/json"))
		{
			res.setContentType("application/json");
			obj = jsonParser.fromXML(inputStr);
		}
		else
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
			return;
		}
		
		// validate uri (/entityname or /entityname/search for searching)
		String[] uri = req.getRequestURI().substring(req.getContextPath().length() + 1).split("/");
		// search?
		if(uri.length == 2 && uri[1].equals("s"))
		{
			Class<?> theClass = annotationChecker.getClassname(uri[0]);
			if(theClass == null)
			{
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
				return;
			}
			if(obj.getClass() != theClass)
			{
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, "uri and search-type do not match");
				return;
			}
			//HibernateConnector hc = new HibernateConnector();
			List<Object> searchResult = hc.searchObject(theClass, obj);
			for(Object result : searchResult)
			{
				// json or xml?
				if(req.getContentType().equals("application/xml"))
				{
					out.print(xmlParser.toXML(result));					
				}else if (req.getContentType().equals("application/json"))
				{
					out.print(jsonParser.toXML(result));
				}
			}
		}
		else		// post new instance to db??
		{
			if(uri.length != 1)
			{
				res.sendError(HttpServletResponse.SC_BAD_REQUEST);
				return;
			}
			
			Class<?> theClass = annotationChecker.getClassname(uri[0]);
			if(theClass == null)
			{
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
				return;
			}
			
			//HibernateConnector hc = new HibernateConnector();
			if(hc.postToHibernate(obj))
				out.println("OK");
			else
				out.println("ERROR");
		}
		
		out.close();
	}
	
	@Override
	public void doPut(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException 
	{
		PrintWriter out = res.getWriter();
		long id = 0;
		
		// validate uri (/entityname/id)
		String[] uri = req.getRequestURI().substring(req.getContextPath().length() + 1).split("/");
		if(uri.length != 2)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		Class<?> theClass = annotationChecker.getClassname(uri[0]);
		if(theClass == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
			return;
		}
		
		try {
			id = Long.parseLong(uri[1]);
		}catch(NumberFormatException nfe)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		String str = "";
		String inputStr = "";
		BufferedReader in = req.getReader();
		Object obj = null;
		while((str = in.readLine()) != null)
			inputStr += str;
		
		// json or xml?
		if(req.getContentType().equals("application/xml"))
		{
			obj = xmlParser.fromXML(inputStr);
			res.setContentType("application/xml");						
		}else if (req.getContentType().equals("application/json"))
		{
			res.setContentType("application/json");
			obj = jsonParser.fromXML(inputStr);
		}
		else
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
			return;
		}
		
		//HibernateConnector hc = new HibernateConnector();
		if(hc.putToHibernate(theClass, obj, id))
			out.println("OK");
		else
			out.println("ERROR");
		
		out.close();
	}
	
	@Override
   public void doDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
   {
		PrintWriter out = res.getWriter();
		long id;
		
		// validate uri (/entityname/id)
		String[] uri = req.getRequestURI().substring(req.getContextPath().length() + 1).split("/");
		if(uri.length != 2)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		try {
			id = Long.parseLong(uri[1]);
		}catch(NumberFormatException nfe)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		Class<?> theClass = annotationChecker.getClassname(uri[0]);
		
		if(theClass == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
			return;
		}
		// perform delete
		//HibernateConnector hc = new HibernateConnector();
		boolean success = hc.deleteFromHibernate(theClass, id);
		
		if(success)
			out.println("OK");
		else
			out.println("ERROR");
	}
	/**
	 * Performs a full text search on fields annotated with @Evs11Serach
	 * 
	 * @param req
	 * @param res
	 * @throws ServletException
	 * @throws IOException
	 */
	private void doFullTextSearch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// Validate path (/entityname?search=)
		String[] uri = req.getRequestURI().substring(req.getContextPath().length() + 1).split("/");
		
		if(uri.length != 1)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		Class<?> theClass = annotationChecker.getClassname(uri[0]);		
		if(theClass == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
			return;
		}
		
		String searchStr = req.getParameter("s");
		if(searchStr == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Uri " + uri[0] + " not found as plural");
			return;
		}
		
		if(req.getContentType() == null)
		{
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
			return;
		}
		if(req.getContentType().equals("application/xml"))
			res.setContentType("application/xml");
		else if(req.getContentType().equals("application/json"))
			res.setContentType("application/json");
		else
		{
			// neither json nor xml -> error
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid content type: " + req.getContentType());
			return;
		}
		
		PrintWriter out = res.getWriter();
		//HibernateConnector hc = new HibernateConnector();
		List<Object> searchResult = hc.searchString(theClass, searchStr, annotationChecker.getSearchableFields(uri[0]));
		for(Object result : searchResult)
		{
			// json or xml?
			if(req.getContentType().equals("application/xml"))
			{
				out.print(xmlParser.toXML(result));					
			}else if (req.getContentType().equals("application/json"))
			{
				out.print(jsonParser.toXML(result));
			}
		}
		out.close();
		return;
	}
}
